package cn.bluech.lib.utils;

/*
 * BD-09 ： 百度坐标系(百度地图)
 * GCJ-02： 火星坐标系（谷歌中国地图、高德地图）
 * WGS84 ： 地球坐标系（国际通用坐标系，谷歌地图）
 */
public class LatlngUtil {
    private final double x_PI = 3.14159265358979324 * 3000.0 / 180.0;
    private final double PI = 3.1415926535897932384626;
    private final double a = 6378245.0;
    private final double ee = 0.00669342162296594323;
    
    // 百度坐标系转火星坐标系
    public double[] bd09ToGcj02(double lonBd, double latBd) {
        double x = lonBd - 0.0065;
        double y = latBd - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);
    
        double lonGcj = z * Math.cos(theta);
        double latGcj = z * Math.sin(theta);
        double[] gcj = { lonGcj, latGcj };//火星坐标系值
    
        //火星坐标系转wgs84
        return gcj02ToWgs84(gcj[0], gcj[1]);
    }
    
    // 火星坐标系转wgs84
    public double[] gcj02ToWgs84(double lonGcj, double latGcj) {
        if (outOfChina(lonGcj, latGcj)) {
            //不在国内，不进行纠偏
            return new double[]{ lonGcj, latGcj };
        } else {
            double dLon = transformLon(lonGcj - 105.0, latGcj - 35.0);
            double dLat = transformLat(lonGcj - 105.0, latGcj - 35.0);
            double latRadian = latGcj / 180.0 * PI;
            double magic = Math.sin(latRadian);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.sqrt(magic);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(latRadian) * PI);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
            double mgLon = lonGcj + dLon;
            double mgLat = latGcj + dLat;
            double lonWgs = lonGcj * 2 - mgLon;
            double latWgs = latGcj * 2 - mgLat;
            return new double[]{ lonWgs, latWgs };
        }
    }
    
    // 火星坐标系转百度坐标系
    public double[] gcj02ToBd09(double lonGcj, double latGcj) {
        double z = Math.sqrt(lonGcj * lonGcj + latGcj * latGcj) + 0.00002 * Math.sin(latGcj * x_PI);
        double theta = Math.atan2(latGcj, lonGcj) + 0.000003 * Math.cos(lonGcj * x_PI);
        double lonBd = z * Math.cos(theta) + 0.0065;
        double latBd = z * Math.sin(theta) + 0.006;
        return new double[]{ lonBd, latBd };
    }
    
    // wgs84转火星坐标系
    public double[] wgs84ToGcj02(double lonWgs, double latWgs) {
        if (outOfChina(lonWgs, latWgs)) {
            //不在国内
            return new double[]{ lonWgs, latWgs };
        }else {
            double dLon = transformLon(lonWgs - 105.0, latWgs - 35.0);
            double dLat = transformLat(lonWgs - 105.0, latWgs - 35.0);
            double latRadian = latWgs / 180.0 * PI;
            double magic = Math.sin(latRadian);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.sqrt(magic);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(latRadian) * PI);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
            double lonGcj = lonWgs + dLon;
            double latGcj = latWgs + dLat;
            return new double[]{ lonGcj, latGcj };
        }
    }
    
    private double transformLon(double lon, double lat) {
        double ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * Math.sqrt(Math.abs(lon));
        ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lon * PI) + 40.0 * Math.sin(lon / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lon / 12.0 * PI) + 300.0 * Math.sin(lon / 30.0 * PI)) * 2.0 / 3.0;
        return ret;
    }
    
    private double transformLat(double lon, double lat) {
        double ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat + 0.2 * Math.sqrt(Math.abs(lon));
        ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }
    
    // 判断是否在国内，不在国内则不做偏移
    private boolean outOfChina(double lon, double lat) {
        return (lon < 72.004 || lon > 137.8347) || ((lat < 0.8293 || lat > 55.8271));
    }
}
